/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Copyright (c) 2005-2009 PRO ACTIVE SAS - www.proactive-group.com
   
  Ref_PicoPass.c
  --------------

  This is the reference applications that shows how to read/write data
  in an Inside Contactless PicoPass tag.
  
  JDA 09/08/2009 : initial release

*/
#include "products/springprox/springprox.h"

#include <stdio.h>
#include <string.h>

SWORD SPROX_Pico_ActivateAny(BYTE uid[8]);
SWORD SPROX_Pico_ActivateAgain(BYTE uid[8]);
SWORD SPROX_Pico_PageSelect(BYTE iso, BYTE page, BYTE data[8]);
SWORD SPROX_Pico_Read(BYTE iso, BYTE addr, BYTE data[8]);
SWORD SPROX_Pico_Read4(BYTE iso, BYTE addr, BYTE data[32]);
SWORD SPROX_Pico_Write(BYTE iso, BYTE addr, BYTE data[8]);
SWORD SPROX_Pico_Halt(void);


int main(int argc, char **argv)
{
  SWORD rc;
  int i;
  char s_buffer[64];
  BYTE uid[8];
  WORD bloc;
  WORD bloc_max = 0;
  BYTE data[8];
  BYTE data_0[8];

  char *device = NULL;

  if (argc > 1)
    device = argv[1];

  printf("SpringCard SDK for K531/SpringProx-CF/CSB Legacy\n");
  printf("\n");
  printf("INSIDE CONTACTLESS PicoPass reference demo\n");
  printf("------------------------------------------\n");
  printf("www.springcard.com\n\n");

  rc = SPROX_GetLibrary(s_buffer, sizeof(s_buffer));
  if (rc != MI_OK)
  {
    printf("Failed to get API version\n");
    goto done;
  }
  printf("API version : %s\n", s_buffer);
  printf("\n");

  /* Open reader */
  /* ----------- */

  rc = SPROX_ReaderOpen(device);
  if (rc != MI_OK)
  {
    printf("Reader not found\n");
    goto done;
  }

  rc = SPROX_ReaderGetDevice(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader found on %s\n", s_buffer);

  rc = SPROX_ReaderGetFirmware(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader firwmare is %s\n", s_buffer);

  /* Configure reader for ISO 14443-B operation */
  /* ----------------------------------------- */
  /* Enter ISO 14443-B mode */
  SPROX_SetConfig(CFG_MODE_ISO_14443_A);
  rc = SPROX_SetConfig(CFG_MODE_ISO_14443_B);
  if (rc != MI_OK)
  {
    printf("Set config failed\n");
    goto done;
  }

  /* RF field ON */
  SPROX_ControlRF(TRUE);

  /* Select a compliant tag */
  /* ---------------------- */

  printf("Put your PicoPass card on the reader...\n");
  for (;;)
  {
    rc = SPROX_Pico_ActivateAny(uid);
    if (rc == MI_OK)
    {
      break;
    }
  }

  printf("Tag found: UID=");
  for (i = 0; i < 8; i++)
    printf("%02X", uid[i]);
  printf("\n");

  /* Read blocks 0 to 255 */

  printf("Reading the card...\n");
  for (bloc = 0; bloc < 256; bloc++)
  {
    rc = SPROX_Pico_Read(FALSE, (BYTE) bloc, data);
    if (rc != MI_OK)
    {
      printf("Failed to read block %d\n", bloc);
      goto tag_halt;
    }

    if (bloc == 0)
    {
      memcpy(data_0, data, 8);
    } else
    if (!memcmp(data_0, data, 8))
    { 
      if (bloc % 16 == 0)
      {
        bloc_max = bloc;
        printf("I guess card has only %d blocks\n", bloc_max);
        break;
      }
    }

    printf("Block %02d : ", bloc);
    for (i=0; i<8; i++)
      printf("%02X", data[i]);
    printf("\n");
  }

tag_halt:
  /* Halt the tag */
  SPROX_Pico_Halt();

close:
  /* Close the reader */
  SPROX_ControlRF(FALSE);
  SPROX_ReaderClose();

done:
  /* Display last error */
  if (rc == MI_OK)
  {
    printf("Done\n");
  } else
  {
    printf("%s (%d)\n", SPROX_GetErrorMessage(rc), rc);
  }
  return 0;
}



/*
 ****************************************************************************
 *
 * INSIDE CONTACTLESS PICOTAG
 *
 ****************************************************************************
 */

#define PICO_CMD_HALT      0x00
#define PICO_CMD_SELECT    0x81
#define PICO_CMD_PAGESEL   0x84
#define PICO_CMD_READ_32   0x06
#define PICO_CMD_ACTALL    0x0A
#define PICO_CMD_IDENTIFY  0x0C
#define PICO_CMD_READ_8    0x0C
#define PICO_CMD_UPDATE    0x87

static WORD Pico_CRC(BYTE dst[2], BYTE *buffer, BYTE len)
{
  WORD crc = 0xE012; /* Preset */
  BYTE j;

  for ( ; len--; )
  {
    crc ^= *(buffer++);
    for (j=8; j--; )
    {
      if (crc & 0x0001)
        crc  = (crc >> 1) ^ 0x8408; /* Polynome */
      else
        crc >>= 1;
    }
  }
  
  if (dst != NULL)
  {
    dst[0] = (BYTE) crc;
    dst[1] = (BYTE) (crc >> 8);
  }

  return crc;
}

SWORD SPROX_Pico_ActivateAny(BYTE uid[8])
{
  BYTE asn[8];
  BYTE buf[16];
  BYTE crc[2];
  WORD len;
  SWORD rc;
  BYTE retry = 10;
  
again:

  /* ACTALL */
  buf[0] = PICO_CMD_ACTALL;
  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 1, buf, &len, FALSE, 256);  
  /* Don't care for rc... */

  Sleep(10);

  /* IDENTIFY */
  buf[0] = PICO_CMD_IDENTIFY;
  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 1, buf, &len, FALSE, 640);
  if (rc == MI_OK)
  {
    if (len == 10)
    {
      Pico_CRC(crc, buf, 8);
      if (memcmp(crc, &buf[8], 2))
        rc = MI_CRCERR;
    } else
      rc = MI_BYTECOUNTERR;
  }

  if (rc == MI_OK)
  { 
    /* Got an Anticollision ID with a valid CRC */
    memcpy(asn, buf, 8);
   
    /* SELECT */
    buf[0] = PICO_CMD_SELECT;
    memcpy(&buf[1], asn, 8);  
    len = sizeof(buf);
    rc = SPROX_B_Exchange(buf, 9, buf, &len, FALSE, 640);

    if (rc == MI_OK)
    {
      if (len == 10)
      {
        Pico_CRC(crc, buf, 8);
        if (!memcmp(crc, &buf[8], 2))
        {
          /* CRC is correct, got a valid UID */
          if (uid != NULL)
          {
            /* UID is transmitted LSB first */
            BYTE i;
            for (i=0; i<8; i++)
              uid[i] = buf[7-i];
          }
        } else
          rc = MI_CRCERR;
      } else
        rc = MI_BYTECOUNTERR;
    }

    if ((rc != MI_OK) && (retry-- > 0))
    {
      Sleep(10);
      goto again;
    }
  }

 
  return rc;
}

SWORD SPROX_Pico_ActivateAgain(BYTE uid[8])
{
  BYTE buf[16];
  BYTE crc[2];
  WORD len;
  SWORD rc;
  BYTE i;
   
  /* SELECT */
  buf[0] = PICO_CMD_SELECT;
  
  /* UID is transmitted LSB first */
  for (i=0; i<8; i++)
    buf[1+i] = uid[7-i];

  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 9, buf, &len, FALSE, 640);
  if (rc == MI_OK)
  {
    if (len == 10)
    {
      Pico_CRC(crc, buf, 8);
      if (!memcmp(crc, &buf[8], 2))
      {
        /* Got an UID with a valid CRC, check it is the one we were expecting */
        for (i=0; i<8; i++)
          if (uid[i] != buf[7-i])
            rc = MI_SERNRERR;
      } else
        rc = MI_CRCERR;
    } else
      rc = MI_BYTECOUNTERR;
  }
  
  return rc;
}

#if 0
SWORD SPROX_Pico_Attrib(void)
{
  BYTE buf[64];
  WORD len;
  SWORD rc;

  memset(buf, 0, sizeof(buf));  
  buf[0] = 0x1D; /* ATTRIB */
  memcpy(&buf[1], &iso3b_tag.atq[0], 4);
  memcpy(&buf[9], &iso3b_tag.atq[4], 4);

  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 15, buf, &len, TRUE, 512);
  if (rc == MI_OK)
  {
    if (len != 3)       rc = MI_BYTECOUNTERR; else
    if (buf[0] != 0x00) rc = MI_CODEERR;
  }
  
  return rc;
}
#endif

SWORD SPROX_Pico_PageSelect(BYTE iso, BYTE page, BYTE data[8])
{
  BYTE buf[64];
  BYTE crc[2];
  WORD len;
  SWORD rc;
  
  buf[0] = PICO_CMD_PAGESEL;
  buf[1] = page;
  Pico_CRC(&buf[2], &buf[1], 1);

  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 4, buf, &len, iso, 1024);
  if (rc == MI_OK)
  {
    if (len != 10)
    {
      rc = MI_BYTECOUNTERR;
    } else
    if (!iso)
    {
      Pico_CRC(crc, buf, 8);
      if (memcmp(crc, &buf[8], 2))
        rc = MI_CRCERR;
    }
  }
  
  if ((rc == MI_OK) && (data != NULL))
    memcpy(data, buf, 8);

  return rc;
}
 
SWORD SPROX_Pico_Read(BYTE iso, BYTE addr, BYTE data[8])
{
  BYTE buf[64];
  BYTE crc[2];
  WORD len;
  SWORD rc;
  BYTE retry = 10;
  
again:

  buf[0] = PICO_CMD_READ_8;
  buf[1] = addr;
  Pico_CRC(&buf[2], &buf[1], 1);

  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 4, buf, &len, iso, 1024);
  if (rc == MI_OK)
  {
    if (len != 10)
    {
      rc = MI_BYTECOUNTERR;
    } else
    if (!iso)
    {
      Pico_CRC(crc, buf, 8);
      if (memcmp(crc, &buf[8], 2))
        rc = MI_CRCERR;
    }
  }

  if ((rc != MI_OK) && (retry-- > 0))
  {
    Sleep(10);
    goto again;
  }
  
  if ((rc == MI_OK) && (data != NULL))
    memcpy(data, buf, 8);

  return rc;
}

SWORD SPROX_Pico_Read4(BYTE iso, BYTE addr, BYTE data[32])
{
  BYTE buf[64];
  BYTE crc[2];
  WORD len;
  SWORD rc;
  
  buf[0] = PICO_CMD_READ_32;
  buf[1] = addr;
  Pico_CRC(&buf[2], &buf[1], 1);

  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 4, buf, &len, iso, 1024);
  if (rc == MI_OK)
  {
    if (len != 34)
    {
      rc = MI_BYTECOUNTERR;
    } else
    if (!iso)
    {
      Pico_CRC(crc, buf, 32);
      if (memcmp(crc, &buf[32], 2))
        rc = MI_CRCERR;
    }
  }

  if ((rc != MI_OK) && (data != NULL))
    memcpy(data, buf, 32);
  
  return rc;
}

SWORD SPROX_Pico_Write(BYTE iso, BYTE addr, BYTE data[8])
{
  BYTE buf[64];
  BYTE crc[2];
  WORD len;
  SWORD rc;
  
  buf[0] = PICO_CMD_UPDATE;
  buf[1] = addr;
  memcpy(&buf[2], data, 8);
  Pico_CRC(&buf[10], &buf[1], 9);

  len = sizeof(buf);
  rc = SPROX_B_Exchange(buf, 12, buf, &len, iso, 1600); /* Tprog up to 15ms */
  if (rc == MI_OK)
  {
    if (len != 10)
    {
      rc = MI_BYTECOUNTERR;
    } else
    if (!iso)
    {
      Pico_CRC(crc, buf, 8);
      if (memcmp(crc, &buf[8], 2))
        rc = MI_CRCERR;
    }
  }
  
  return rc;
}


SWORD SPROX_Pico_Halt(void)
{
  BYTE buf[16];
  WORD len;
  
  /* HALT */
  buf[0] = PICO_CMD_HALT;
  len = sizeof(buf);
  SPROX_B_Exchange(buf, 1, buf, &len, FALSE, 106);
  
  return MI_OK;
}

